/*******************************************************
 * 函数名：ShowIP
 * 描述  ：滑动显示当前设备IP
 * 参数  ：无
 * 返回值：无
 **********************************************************/
void ShowIP()
{
    int sa[4], r=0, t=0;
    String oneLine = WiFi.localIP().toString()+".";
    int s1,s2,s3;
    int ipx,x = 32;
    uint16_t ipcolor=hsv2rgb(hue, saturation, value);
    for (int i=0; i < oneLine.length(); i++)
    { 
      if(oneLine.charAt(i) == '.') 
      { 
        sa[t] = oneLine.substring(r, i).toInt();
        r=(i+1); 
        t++; 
      }
    }
    for(int j=0;j<85;j++){
      ipx = x;
      for(int i=0; i < 4; i++){
        s1 = sa[i]/100;
        s2 = (sa[i] - s1*100)/10;
        s3 = (sa[i] - s1*100)%10;
        if(sa[i]>99){
          showCharacter(ipx, 2, s1 + 48, Clock3x5, ipcolor);
          ipx += 4;
          showCharacter(ipx, 2, s2 + 48, Clock3x5, ipcolor);
          ipx += 4;
          showCharacter(ipx, 2, s3 + 48, Clock3x5, ipcolor);
        }else if(sa[i]>9){
          showCharacter(ipx, 2, s2 + 48, Clock3x5, ipcolor);
          ipx += 4;
          showCharacter(ipx, 2, s3 + 48, Clock3x5, ipcolor);
        }else{
          showCharacter(ipx, 2, s3 + 48, Clock3x5, ipcolor);
        }
        if(i<3){
          ipx += 4;
          matrix.drawPixel(ipx,6,ipcolor);
          ipx += 2;
        }
      }
      if(--x < -90) {
        x = matrix.width();
      }
      matrix.show();
      system_soft_wdt_feed();
      delay(80);
      matrix.clear();
    }
    matrix.clear();
}

void InitServer(){
  // start MDNS
  if (MDNS.begin(MDNS_NAME)) {
    Serial.println("MDNS responder started.");
  }
  // start web server
  // handle index
  server.on("/", []() {
    // send index.html
    server.send(200, "text/html; charset=utf-8", "<!DOCTYPE html><html lang='cn'><head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width,initial-scale=1,user-scalable=0'> <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/uikit@3.15.19/dist/css/uikit.min.css' /> <script src='https://cdn.jsdelivr.net/npm/uikit@3.15.19/dist/js/uikit.min.js'></script> <script src='https://cdn.jsdelivr.net/npm/uikit@3.15.19/dist/js/uikit-icons.min.js'></script> <script src='https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js'></script> <title>像素时钟</title> <style> body { text-align: center; font-size: 1.5em; background-repeat: no-repeat; animation: 20s move linear infinite; } P { height: 10px; } input { background:rgba(255,255,255,0); } </style></head><body> <ul class='uk-subnav uk-subnav-pill uk-flex-center' uk-switcher> <li><a href='#'>主页</a></li> <li><a href='#'>模式</a></li> <li><a href='#'>配置</a></li> <li><a href='#'>风格</a></li> <li><a href='#'>说明</a></li> </ul>  <ul class='uk-switcher uk-margin'> <li> <div> <h1>主页</h1> <div class='flex one center'> <div > <img id='emojis' onclick='clickEmoji(event);' style='height: 122px;width: 131px;margin: 0 auto; text-align: center;' src=''>  </div> </div> <h1 id='out' style='margin: 0 auto; text-align: center;'></h1> <div style='margin: 0 auto; text-align: center;'> <canvas id='emoji' width='8' height='8'></canvas> </div> <script> var connection = new WebSocket('ws://' + location.hostname + ':81/', ['arduino']); connection.onopen = function () { connection.send('Connect ' + new Date()); };  connection.onerror = function (error) { console.log('WebSocket Error ', error); };  connection.onmessage = function (e) { console.log('Server: ', e.data);  if(e.data[0]=='{'){ console.log('json'); var jsonOb = eval('('+e.data+')'); var brightNess = typeof(jsonOb.brightNess)=='undefined'? 150:jsonOb.brightNess; var mode = typeof(jsonOb.mode)=='undefined'? 2:jsonOb.mode; var huea = typeof(jsonOb.huea)=='undefined'? 180:jsonOb.huea; var hued = typeof(jsonOb.hued)=='undefined'? 180:jsonOb.hued; var hueh = typeof(jsonOb.hueh)=='undefined'? 180:jsonOb.hueh; var huem = typeof(jsonOb.huem)=='undefined'? 180:jsonOb.huem; var hues = typeof(jsonOb.hues)=='undefined'? 180:jsonOb.hues; var huew = typeof(jsonOb.huew)=='undefined'? 180:jsonOb.huew; var hueb = typeof(jsonOb.hueb)=='undefined'? 180:jsonOb.hueb; var clockMode = typeof(jsonOb.clockMode)=='undefined'? 1:jsonOb.clockMode; var dateMode = typeof(jsonOb.dateMode)=='undefined'? 1:jsonOb.dateMode; var loopMode = typeof(jsonOb.loopMode)=='undefined'? 1:jsonOb.loopMode; var bFansMode = typeof(jsonOb.bFansMode)=='undefined'? 1:jsonOb.bFansMode; $(\"input[name='radio-mode']\").eq(mode-1).attr('checked','checked'); $(\"input[name='radio-clock']\").eq(clockMode-1).attr('checked','checked'); $(\"input[name='radio-date']\").eq(dateMode-1).attr('checked','checked'); $(\"input[name='radio-loop']\").eq(loopMode-1).attr('checked','checked'); $(\"input[name='radio-bf']\").eq(bFansMode-1).attr('checked','checked'); document.getElementById('bt').value=parseInt(brightNess); document.getElementById('bt_value').value=parseInt(brightNess); document.getElementById('huea').value=parseInt(huea); document.getElementById('huea_value').value=parseInt(huea); document.getElementById('hued').value=parseInt(hued); document.getElementById('hued_value').value=parseInt(hued); document.getElementById('hueh').value=parseInt(hueh); document.getElementById('hueh_value').value=parseInt(hueh); document.getElementById('huem').value=parseInt(huem); document.getElementById('huem_value').value=parseInt(huem); document.getElementById('hues').value=parseInt(hues); document.getElementById('hues_value').value=parseInt(hues); document.getElementById('huew').value=parseInt(huew); document.getElementById('huew_value').value=parseInt(huew); document.getElementById('hueb').value=parseInt(hueb); document.getElementById('hueb_value').value=parseInt(hueb);  } }; function clickEmoji(e) {  var xo = 3; var yo = 3; var xs = 13; var ys = 12; var x = e.offsetX;  var y = e.offsetY; var c = Math.round((x - xo - 4) / xs);  var r = Math.round((y - yo - 4) / ys);  document.getElementById('out').innerText = 'Selected [' + (c + 1) + ', ' + (r + 1) + ']';  var img = document.getElementById('emojis');  var ce = document.getElementById('emoji'); var ctx = ce.getContext('2d');  ctx.drawImage(img, -(xo + (xs * c)), -(yo + (ys * r)), 131, 122);  var index = 'p' + c + '' + r; console.log('index: ' + index);  connection.send(index);  } function FirmwareInfo(){ console.log('FirmwareInfo: F');  connection.send('F'); } </script> <div class='flex one center'> <label> <button class='uk-button uk-button-primary' onclick='FirmwareInfo()'>固件信息</button> </label> </div> </div> </li> <li> <div> <h2 class='uk-flex-center'>模式选择</h2> <div class='uk-flex-center'> <label> <input type='radio' onclick='changeMode(1);' name='radio-mode'> <span class='checkable'>时钟</span> </label> <br> <label> <input type='radio' onclick='changeMode(2);' name='radio-mode'> <span class='checkable'>日期</span> </label> <br> <label> <input type='radio' onclick='changeMode(3);' name='radio-mode'> <span class='checkable'>时钟+日期</span> </label> <br> <label> <input type='radio' onclick='changeMode(4);' name='radio-mode'> <span class='checkable'>天气</span> </label> <br> <label> <input type='radio' onclick='changeMode(5);' name='radio-mode'> <span class='checkable'>代码雨</span> </label> <br> <label> <input type='radio' onclick='changeMode(6);' name='radio-mode'> <span class='checkable'>B站粉丝数</span> </label> <br> <label> <input type='radio' onclick='changeMode(7);' name='radio-mode'> <span class='checkable'>时光回溯</span> </label> </div> <script> function changeMode(num) {  var modelNum = 'm' + parseInt(num).toString(16);  console.log('Model: ' + modelNum);  connection.send(modelNum); } function nextMode(num) {  var modelNum = 'mc';  console.log('Model: ' + modelNum);  connection.send(modelNum); } function FlashBack(){ changeMode(7); } </script> <div class='uk-flex-center'> <label> <button class='uk-button uk-button-primary' onclick='nextMode()'>模式Next</button> </label> </div> <div class='uk-flex-center'> <label> <button class='uk-button uk-label-success' onclick='FlashBack()'>时光回溯</button> </label> </div> </div> </li> <li> <div> <h1>亮度</h1> <div class='range'> <ul class='uk-flex-right'> <label>亮度</label> <label><input id='bt' type='range' min='0' max='255' step='1' oninput='sendBrightness()'/></label>  <label><input id='bt_value' type='text' style='border: 0;' value='125'/> </label> </ul> </div> <h1>颜色</h1> <div class='range'> <ul class='uk-flex-center'> <label>全局</label> <label><input id='huea' type='range' min='0' max='360' step='1' oninput='sendHue(\"a\")' /></label>  <label><input id='huea_value' name='hue_value' type='text' style='border: 0;' value='180'/> </label> </ul> </div> <div class='range'> <ul class='uk-flex-right'> <label>冒号</label> <label><input id='hued' type='range' name='hue_value' min='0' max='360' step='1' oninput='sendHue(\"d\")' /></label>  <label><input id='hued_value' name='hue_value' type='text' style='border: 0;' value='180'/> </label> </ul> </div> <div class='range'> <ul class='uk-flex-right'> <label>小时</label> <label><input id='hueh' type='range' name='hue_value' min='0' max='360' step='1' oninput='sendHue(\"h\")' /></label>  <label><input id='hueh_value' name='hue_value' type='text' style='border: 0;' value='180'/> </label> </ul> </div> <div class='range'> <ul class='uk-flex-right'> <label>分钟</label> <label><input id='huem' type='range' name='hue_value' min='0' max='360' step='1' oninput='sendHue(\"m\")' /></label>  <label><input id='huem_value' name='hue_value' type='text' style='border: 0;' value='180'/> </label> </ul> </div> <div class='range'> <ul class='uk-flex-right'> <label>秒钟</label> <label><input id='hues' type='range' name='hue_value' min='0' max='360' step='1' oninput='sendHue(\"s\")' /></label>  <label><input id='hues_value' name='hue_value' type='text' style='border: 0;' value='180'/> </label> </ul> </div> <div class='range'> <ul class='uk-flex-right'> <label>星期</label> <label><input id='huew' type='range' name='hue_value' min='0' max='360' step='1' oninput='sendHue(\"w\")' /></label>  <label><input id='huew_value' name='hue_value' type='text' style='border: 0;' value='180'/> </label> </ul> </div> <div class='range'> <ul class='uk-flex-right'> <label>粉丝</label> <label><input id='hueb' type='range' name='hue_value' min='0' max='360' step='1' oninput='sendHue(\"b\")' /></label>  <label><input id='hueb_value' name='hue_value' type='text' style='border: 0;' value='180'/> </label> </ul> </div> <script> function sendBrightness() {  var Obrightness = document.getElementById('bt').value;  var oMessage = 's';  if (Obrightness.length == 1) {  oMessage = oMessage + '00';  } else if (Obrightness.length == 2) {  oMessage = oMessage + '0';  }  document.getElementById('bt_value').value=parseInt(Obrightness ? Obrightness : 999); oMessage = oMessage + parseInt(Obrightness ? Obrightness : 999);  console.log('oMessage: ' + oMessage); connection.send(oMessage);  } function sendHue(n) {  var OHueValue = document.getElementById('hue'+n).value;  var oMessage = 'h'+n;  if (OHueValue.length == 1) {  oMessage = oMessage + '00';  } else if (OHueValue.length == 2) {  oMessage = oMessage + '0';  }  if(n=='a'){ var els =document.getElementsByName('hue_value'); for (var i = 0, j = els.length; i < j; i++){ els[i].value = OHueValue; } }else{ document.getElementById('hue'+n+'_value').value=parseInt(OHueValue ? OHueValue : 360); } oMessage = oMessage + parseInt(OHueValue ? OHueValue : 999);  console.log('oMessage: ' + oMessage); connection.send(oMessage);  } function configSave() { var oMessage = 'c';  console.log('oMessage: ' + oMessage); connection.send(oMessage);  } </script> <ul class='flex two center'> <label> <button class='uk-button uk-button-primary' onclick='configSave()' style='right: 0px;'>保存配置</button> </label> </ul>  </div> </li> <li> <div> <h2>时钟风格</h2> <ul class='flex one'> <label> <input type='radio' onclick='clockMode(1);' name='radio-clock'> <span class='checkable'>HH:mm:ss</span> </label> <br> <label> <input type='radio' onclick='clockMode(2);' name='radio-clock'> <span class='checkable'>HH:mm</span> </label> <br> <label> <input type='radio' onclick='clockMode(3);' name='radio-clock'> <span class='checkable'>emoji HH:mm</span> </label> <br> <label> <input type='radio' onclick='clockMode(4);' name='radio-clock'> <span class='checkable'>emoji HH:mm</span> </label> </ul> <h2>日期风格</h2> <ul class='flex one'> <label> <input type='radio' onclick='DateModel(1);' name='radio-date'> <span class='checkable'>MM/DD</span> </label> <br> <label> <input type='radio' onclick='DateModel(2);' name='radio-date'> <span class='checkable'>emoji MM-DD</span> </label> </ul> <h2>切换风格</h2> <ul class='flex one'> <label> <input type='radio' onclick='LoopModel(1);' name='radio-loop'> <span class='checkable'>上下滑动</span> </label> <br> <label> <input type='radio' onclick='LoopModel(2);' name='radio-loop'> <span class='checkable'>渐隐渐显</span> </label> </ul> <h2>粉丝数风格</h2> <ul class='flex one'> <label> <input type='radio' onclick='BFansModel(1);' name='radio-bf'> <span class='checkable'>单一颜色</span> </label> <br> <label> <input type='radio' onclick='BFansModel(2);' name='radio-bf'> <span class='checkable'>彩色</span> </label> </ul> </div> <script> function clockMode(num) { var styleNum = 't' + parseInt(num).toString(16); console.log('时钟风格: ' + styleNum);  connection.send(styleNum); } function DateModel(num) { var styleNum = 'd' + parseInt(num).toString(16); console.log('日期风格: ' + styleNum);  connection.send(styleNum); } function LoopModel(num) { var styleNum = 'l' + parseInt(num).toString(16); console.log('切换风格: ' + styleNum);  connection.send(styleNum); } function BFansModel(num) { var styleNum = 'b' + parseInt(num).toString(16); console.log('粉丝数风格: ' + styleNum);  connection.send(styleNum); } </script> <ul class='flex two center'> <label> <button class='uk-button uk-button-primary' onclick='configSave()' style='right: 0px;'>保存配置</button> </label> </ul> </li> <li> <div> <div class='flex one center'> <h4>说明</h4> <h3>XMatrix像素时钟</h3> <p>作者：亚里士不可得</p> <p>制作时间：2022/12/27</p> <p>版本：2.7</p> </div> </div> </li> </ul> </body></html>");
    Serial.println("index.html sent.");
  });
  server.begin();

  // start webSocket server
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
  // Add service to MDNS
  MDNS.addService("http", "tcp", 80);
  MDNS.addService("ws", "tcp", 81);
}

void WebServer_run(){
  MDNS.update();
  webSocket.loop();
  server.handleClient();  
}

/*******************************************************
 * 函数名：webSocketEvent
 * 描述  ：处理网页webSocket数据，事件回调函数
 * 参数  ：
 *         @num 设备序号
 *         @type 消息类型
 *         @payload 消息内容
 *         @length 消息长度
 * 返回值：无
 **********************************************************/
void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
{
    switch (type)
    {
    case WStype_DISCONNECTED:
        Serial.printf("[%u] Disconnected!\n", num);
        break;
    case WStype_CONNECTED:
    {
        IPAddress ip = webSocket.remoteIP(num);
        Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
        webSocket.sendTXT(num, "Connected"); // send message to client
        configSync(num); // 配置同步
    }
    break;
    case WStype_TEXT:
    {
        Serial.printf("[%u] get Text: %s\n", num, payload);
        if(((char *)payload)[0] == '{'||((char *)payload)[0] == 's') 
        {
          strcpy(settingMessage,(char *)payload);
          if (settingMessage[0] == 's')//亮度
          {
             int bt= ((int)settingMessage[1]-48)*100+((int)settingMessage[2]-48)*10+(int)settingMessage[3]-48;
             if(bt>=0&&bt<=255) brightnessNow = bt;
             matrix.setBrightness(brightnessNow);
             matrix.show();
          }
          else if(settingMessage[0] == '{')
          {
             handleSettingMessage();
          }
        }
        else if (((char *)payload)[0] != 'C') 
        {
          strcpy(socketMessage, (char *)payload);
          if (socketMessage[0] == 'p')
          {
              matrix.clear();
              pic_x = ((int)socketMessage[2]) - 47;
              pic_y = ((int)socketMessage[1]) - 47;
              SetEmoji(pic_x, pic_y);
              matrix.show();
          }
          else if (socketMessage[0] == 'F')//显示固件信息
          {
              showFirmwareInfo(3, 1, true);
              delay(500);
              showFirmwareInfoEnd();
          }
          else if (socketMessage[0] == 't')//时钟风格
          {
              int cm = (int)socketMessage[1]-48;
              matrix.clear();
              clockMode = cm<5? cm:1;
          }
          else if (socketMessage[0] == 'd')//日期风格
          {
              int dm = (int)socketMessage[1]-48;
              matrix.clear();
              dateMode = dm<3? dm:1;
          }
          else if (socketMessage[0] == 'l')//切换风格
          {
              int dm = (int)socketMessage[1]-48;
              loopMode = dm<3? dm:1;
              modeChange(Mode, Mode);
          }
          else if (socketMessage[0] == 'b')//粉丝数风格
          {
              int dm = (int)socketMessage[1]-48;
              bFansMode = dm<3? dm:1;
              modeChange(Mode, Mode);
          }
          else if(socketMessage[0] == 'm')//模式变更
          {
            if(socketMessage[1] == 'c'){//下一个模式
              nextMode();
              configSync(num); // 配置同步
            }else{
              int m = (int)socketMessage[1]-48;
              toTargetMode(m);
            } 
          }else if(socketMessage[0] == 'h'){//颜色设置
                int hh= ((int)socketMessage[2]-48)*100+((int)socketMessage[3]-48)*10+(int)socketMessage[4]-48;
                Serial.print("传入色值：");
                Serial.println(hh);
                if(hh>=0&&hh<=360){  
                    switch (socketMessage[1])
                    {
                    case 'a':
                        hue = hh;
                        hueh = hh;
                        huem = hh;
                        hues = hh;
                        huew = hh;
                        hueb = hh;
                        break;
                    case 'd':
                        hue = hh;
                        break;
                    case 'h':
                        hueh = hh;
                        break;
                    case 'm':
                        huem = hh;
                        break;
                    case 's':
                        hues = hh;
                        break;   
                    case 'w':
                        huew = hh;
                        break;   
                    case 'b':
                        hueb = hh;
                        break;             
                    default:
                        break;
                    }
                }
          }else if(socketMessage[0] == 'c'){
              configSave();
          }else if(socketMessage[0] == 'o'){
            if(Mode == DRAW_MODE){
              Serial.println("模式正确");
              for (uint8_t y = 0; y < yres; y++) {
                for (uint8_t x = 0; x < xres; x++) {
                  uint8_t r = payload[y*xres*3+x*3+1]*2;
                  uint8_t g = payload[y*xres*3+x*3+2]*2;
                  uint8_t b = payload[y*xres*3+x*3+3]*2;
                  matrix.drawPixel(x,y,matrix.Color(r,g,b));
                }
              }
              //Serial.printf("(%u, %u) color: %u, %u, %u\n", x, y, r, g, b);
              matrix.show();
            }else{
              Serial.println("模式错误");
            }
          }
        }
    }
    break;
    }
}
